package persistence;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javassist.util.proxy.ProxyFactory;

public class PersistentClass {
	private String name;
	private Class<?> clazz;
	private String tableName;
	private List<PersistentProperty> persistentProperties = new ArrayList<PersistentProperty>();
	private PersistentProperty id;
	private StringBuffer stringBuffer = new StringBuffer();
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Class<?> getClazz() {
		return clazz;
	}

	public void setClazz(Class<?> clazz) {
		this.clazz = clazz;
	}

	public String getTableName() {
		return tableName;
	}

	public void setTableName(String tableName) {
		this.tableName = tableName;
	}

	public List<PersistentProperty> getPersistentProperties() {
		return persistentProperties;
	}

	public void setPersistentProperties(
			List<PersistentProperty> persistentProperties) {
		this.persistentProperties = persistentProperties;
	}

	public PersistentProperty getId() {
		return id;
	}

	public void setId(PersistentProperty id) {
		this.id = id;
	}
	
	public boolean isProxied() {
		//return Proxy.isProxyClass(clazz);
		return ProxyFactory.isProxyClass(clazz);
	}
	
	public void parse() throws PersistenceException {
	    try {
	    	clazz = Class.forName(getName());
	    } catch (ClassNotFoundException e) {
	    	throw new PersistenceException("Mapped class not found: " + getName());
	    }
	    setTableName(clazz.getSimpleName().toLowerCase()); // par defaut

	    // Iterer sur les méthodes get  pour déterminer les attributs persistants
	    Method[] methods = clazz.getMethods();
	    for (int i = 0; i < methods.length; i++) {
	    	Method method = methods[i];
	    	String methodName = method.getName();
	    	
	    	if (methodName.startsWith("get") // Une méthode get
	          && method.getParameterTypes().length == 0 // Sans paramètres
	          && method.getDeclaringClass().equals(clazz) // Non héritée
	          && method.getAnnotation(Transient.class) == null) { // Et non Transient
	    		PersistentProperty property;
	    		if(method.getAnnotation(ManyToOne.class) != null) { // C'est un ManyToOne
	  	    		property = new ManyToOneProperty(this, method);
	  	    	}
	  	    	else {
	  	    		property = new PersistentProperty(this, method);
	  	    	}
	    		addPersistentProperty(property);
	    	}
	    }
	}

	private void addPersistentProperty(PersistentProperty property) {
		getPersistentProperties().add(property);
	}

	public PersistentProperty getPersistentProperty(String string) {
		Iterator<PersistentProperty> ite = persistentProperties.iterator();
		PersistentProperty pp = null;
		while(ite.hasNext()) {
			pp = (PersistentProperty)ite.next();
			if(pp.getName().equals(string)) {
				return pp;
			}
		}
		return null;
	}

	public String getCreateTableStatement() {
		// create table book(id  BIGINT GENERATED ALWAYS AS IDENTITY,title VARCHAR(255),price DECIMAL(10,2),category_id BIGINT)
		stringBuffer.setLength(0);
		stringBuffer.append("CREATE TABLE " + this.getTableName() + " (");
		Iterator<PersistentProperty> ite = persistentProperties.iterator();
		while(ite.hasNext()) {
			PersistentProperty pp = (PersistentProperty)ite.next();
			stringBuffer.append(pp.getColumnName() + " " + pp.getSqlType());
			if(ite.hasNext()) {
				stringBuffer.append(", ");
			}
		}
		stringBuffer.append(")");
		return stringBuffer.toString();
	}

	public String getDropTableStatement() {
		// drop table book
		return "DROP TABLE " + this.getTableName();
	}

	public String getInsertStatement() {
		// insert into book(title,price,category_id) values (?,?,?)
		stringBuffer.setLength(0);
		stringBuffer.append("INSERT INTO " + this.getTableName() + " (");
		Iterator<PersistentProperty> ite = persistentProperties.iterator();
		while(ite.hasNext()) {
			PersistentProperty pp = (PersistentProperty)ite.next();
			if(!pp.isId()) {
				stringBuffer.append(pp.getColumnName());
				if(ite.hasNext()) {
					stringBuffer.append(", ");
				}
			}
		}
		stringBuffer.append(") VALUES (");
		for(int i = 0; i < this.getPersistentProperties().size()-1; i++) {
			stringBuffer.append("?");
			if(i < (this.getPersistentProperties().size()-2)) {
				stringBuffer.append(", ");
			}
		}
		stringBuffer.append(")");
		return stringBuffer.toString();
	}

	public String getUpdateStatement() {
		// update book set title = ?,price = ?,category_id = ? where id = ?
		stringBuffer.setLength(0);
		StringBuffer sb = new StringBuffer();
		stringBuffer.append("UPDATE " + this.getTableName() + " SET ");
		
		Iterator<PersistentProperty> ite = persistentProperties.iterator();
		while(ite.hasNext()) {
			PersistentProperty pp = (PersistentProperty)ite.next();
			if(pp.isId()) {
				sb.append((sb.length() == 0) ? pp.getColumnName() + " = ?" : " AND " + pp.getColumnName() + " = ?");
			}				
			else {
				stringBuffer.append(pp.getColumnName() + " = ?");
				if(ite.hasNext()) {
					stringBuffer.append(", ");
				}
			}
		}
		stringBuffer.append(" WHERE " + sb.toString());
		return stringBuffer.toString();
	}

	public String getSelectAllStatement() {
		// select * from book
		return "SELECT * FROM " + this.getTableName();
	}
	
	public String getSelectByPropertyStatement(PersistentProperty persProp) {
		// select * from book where title =  ?
		return this.getSelectAllStatement() + " WHERE " + persProp.getColumnName() + " = ?";
	}

	public String getSelectByIdStatement() {
		// select * from book where id =  ?
		return this.getSelectAllStatement() + " WHERE " + this.id.getColumnName() + " = ?";
	}
	
	public String getDeleteStatement() {
		// delete from book where id = ?
		return "DELETE FROM " + this.getTableName() + " WHERE " + this.id.getColumnName() + " = ?";
	}

	public Object createInstance() throws PersistenceException {
		try {
			return clazz.newInstance();
		}
		catch (InstantiationException e) {
			throw new PersistenceException("Can't init instance.");
		}
		catch (IllegalAccessException e) {
			throw new PersistenceException("Illegal access instance.");
		}
	}
}
